import {
  FC,
  useState,
  ChangeEvent, // 输入框改变事件
  KeyboardEvent, // 键盘事件
  ReactElement,
  useEffect,
  useRef
} from 'react'
import classNames from 'classnames'
import Input, { InputProps } from '../Input/input'
import Icon from '../Icon/icon'
import Transition from '../Transition/transition'
import { useDebounce } from '../../hooks/useDebounce'
import useClickOutside from '../../hooks/useClickOutside'

export type DataSourceType<T = {}> = T & {
  value: string
}
// https://api:github.com/search/users?q=ab
export interface AutoCompleteProps extends Omit<InputProps, 'onSelect' | 'onChange'> {
  fetchSuggestions: (str: string) => DataSourceType[] | Promise<DataSourceType[]>
  /** 点击选中建议项时触发的回调*/
  onSelect?: (item: DataSourceType) => void
  /** 文本框发生改变的时候触发的事件*/
  onChange?: (value: string) => void
  /**支持自定义渲染下拉项*/
  renderOption?: (item: DataSourceType) => ReactElement
}

export const AutoComplete: FC<AutoCompleteProps> = (props) => {
  const {
    fetchSuggestions,
    onSelect,
    onChange,
    value, // 初始值
    renderOption, // 支持自定义渲染下拉项
    ...restProps
  } = props
  const [inputValue, setInputValue] = useState(value as string) // 输入框的值
  const [suggestions, setSugestions] = useState<DataSourceType[]>([]) // 下拉列表
  const [loading, setLoading] = useState(false)
  const [showDropdown, setShowDropdown] = useState(false) // 是否显示下拉列表
  const [highlightIndex, setHighlightIndex] = useState(-1) // 高亮索引
  const triggerSearch = useRef(false) // 是否触发搜索
  const nodeRef = useRef(null) // css transition ref
  const componentRef = useRef<HTMLDivElement>(null) // 组件ref
  const debouncedValue = useDebounce(inputValue, 300) // 防抖

  // 点击组件外区域
  useClickOutside(componentRef, () => {
    setSugestions([]) // 清空下拉列表
  })

  useEffect(() => {
    // triggerSearch 限制回车，选择时值发生不改变，导致重新渲染
    if (debouncedValue && triggerSearch.current) {
      setSugestions([]) // 清空下拉列表
      const results = fetchSuggestions(debouncedValue)
      if (results instanceof Promise) {
        setLoading(true)
        results.then((data) => {
          setLoading(false)
          setSugestions(data) // 设置下拉列表
          if (data.length > 0) setShowDropdown(true)
        })
      } else {
        setSugestions(results) // 设置下拉列表
        setShowDropdown(true) // 显示下拉列表
        if (results.length > 0) setShowDropdown(true)
      }
    } else {
      setShowDropdown(false)
    }
    setHighlightIndex(-1) // 重置高亮索引
  }, [debouncedValue, fetchSuggestions])

  // 计算高亮索引
  const highlight = (index: number) => {
    if (index < 0) index = 0
    if (index >= suggestions.length) {
      index = suggestions.length - 1
    }
    setHighlightIndex(index)
  }

  // 键盘事件
  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    switch (e.keyCode) {
      case 13: // 回车
        if (suggestions[highlightIndex]) {
          handleSelect(suggestions[highlightIndex]) // 选中高亮项
        }
        break
      case 38: // 向上
        highlight(highlightIndex - 1)
        break
      case 40: // 向下
        highlight(highlightIndex + 1)
        break
      case 27: // esc
        setShowDropdown(false)
        break
      default:
        break
    }
  }

  // 当输入框的值发生变化的时候，触发onChange事件
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value.trim()
    setInputValue(value) // 设置输入框的值
    onChange?.(value) // 触发onChange事件
    triggerSearch.current = true // 触发搜索
  }

  // 提示框点击
  const handleSelect = (item: DataSourceType) => {
    setInputValue(item.value) // 设置输入框的值
    setShowDropdown(false) // 隐藏下拉列表
    onSelect?.(item)
    triggerSearch.current = false // 取消搜索
  }

  // 自定义展示内容
  const renderTemplate = (item: DataSourceType) => {
    return renderOption ? renderOption(item) : item.value
  }

  // 生成下拉列表
  const generateDropdown = () => {
    return (
      <Transition
        in={showDropdown || loading}
        animation="zoom-in-top"
        timeout={300}
        onExited={() => {
          setSugestions([])
        }}
        nodeRef={nodeRef}
      >
        <ul className="dog-suggestion-list" ref={nodeRef}>
          {loading && (
            <div className="suggstions-loading-icon">
              <Icon icon="spinner" spin />
            </div>
          )}
          {suggestions.map((item, index) => {
            const cnames = classNames('suggestion-item', {
              'is-active': index === highlightIndex
            })
            return (
              <li key={index} className={cnames} onClick={() => handleSelect(item)}>
                {renderTemplate(item)}
              </li>
            )
          })}
        </ul>
      </Transition>
    )
  }
  return (
    <div className="dog-auto-complete" ref={componentRef}>
      <Input
        value={inputValue}
        onChange={handleChange} // 输入框事件
        onKeyDown={handleKeyDown} // 键盘事件
        {...restProps}
      />
      {generateDropdown()}
    </div>
  )
}

export default AutoComplete
